package app;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.ResourceBundle;
import java.util.Vector;

import javax.swing.JOptionPane;

import util.ChangingModel;
import util.DBFunctions;
import util.PasswordFunctions;
import util.Statistic;
import util.StringStringPair;
import util.ValueStringPair;
import util.ValuesForComboBoxes;
import bundles.BundleLoader;

import com.db4o.ObjectContainer;

import definitions.Constants;
import entities.Address;
import entities.Event;
import entities.Member;
import entities.Participant;
import entities.Training;
import entities.User;

/**
 * Model of this applications. Manages selected Members, Events, Stats,
 * Particpants and Adress. Also a Faccade for DB Functions. Gets orders by the
 * controller.
 * 
 * @author MK
 * 
 */
public class Model extends ChangingModel {

	private User loggedInUser;
	private Member selectedMember;
	private Event selectedEvent;
	private String selectedStat;
	// private Participant selectedParticipant;
	private Address selectedAddress;

	public Model() {

		this.loggedInUser = null;
		this.selectedEvent = null;
		this.selectedMember = null;
		this.selectedStat = null;
		// this.selectedParticipant = null;
	}

	public Member getSelectedMember() {
		return selectedMember;
	}

	public Event getSelectedEvent() {
		return selectedEvent;
	}

	public String getSelectedStat() {
		return selectedStat;
	}

	public Vector<Vector<String>> getNearEventEntries() {
		return DBFunctions.getNearEventEntries(15);
	}

	public Vector<String> getEventAttributeNames() {

		return Event.attributeNames();
	}

	public Vector<Vector<String>> getHealthyMemberEntries() {
		return DBFunctions.getHealthyMemberEntries();
	}

	public Vector<String> getMemberAttributeNames() {

		return Member.attributeNames();
	}

	public Vector<String> getMemberAttributeNamesShort() {
		return Member.attributeNamesShort();
	}

	public Vector<Vector<String>> getNotHealthyMemberEntries() {
		return DBFunctions.getNotHealthyMemberEntries();
	}

	public Vector<Vector<String>> getEventEntries() {
		return DBFunctions.getEventEntries();
	}

	public Vector<Vector<String>> getMemberEntries() {
		return DBFunctions.getMemberEntries();
	}

	public ValueStringPair[] getMonthValues() {
		return ValuesForComboBoxes.getMonthValues();
	}

	public String[] getDaysOfMonthValues() {
		return ValuesForComboBoxes.getDaysOfMonthValues();
	}

	public ValueStringPair[] getTimeValues() {
		return ValuesForComboBoxes.getTimeValues();
	}

	public String[] getAddressValues() {
		return ValuesForComboBoxes.getAddressValues();
	}

	public Vector<Vector<String>> getParticipantsForEvent() {

		if (selectedEvent != null) {

			ArrayList<Participant> participants = selectedEvent
					.getParticipants();

			Vector<Vector<String>> rowData = new Vector<Vector<String>>();

			for (Participant participant : participants) {
				if (participant != null) {
					rowData.add(participant.asShortVector());
				}
			}
			return rowData;
		} else
			return null;
	}

	public StringStringPair[] getStateValues() {
		return ValuesForComboBoxes.getStateValues();
	}

	public StringStringPair[] getPositionValues() {
		return ValuesForComboBoxes.getPositionValues();
	}

	public StringStringPair[] getRoleValues() {
		return ValuesForComboBoxes.getRoleValues();
	}

	public String[] getTrainingDaysValues() {
		return ValuesForComboBoxes.getTrainingDaysValues();
	}

	public void setSelectedEvent(Event selectedEvent) {
		this.selectedEvent = selectedEvent;
	}

	// private Participant getSelectedParticipant() {
	// return selectedParticipant;
	// }
	//
	// private void setSelectedParticipant(Participant selectedParticipant) {
	// this.selectedParticipant = selectedParticipant;
	// }

	public void setSelectedMember(Member selectedMember) {
		this.selectedMember = selectedMember;
	}

	public void setSelectedStat(String selectedStat) {
		this.selectedStat = selectedStat;
	}

	public User checkLoginData(String username, String passwordAsMD5) {
		User userSearchDummy = new User(username, passwordAsMD5, null);
		return (User) DBFunctions.getObject(userSearchDummy);
	}

	public Event searchEvent(Date date) {

		Event searchEvent = new Event(date.getTime(), null, null);
		Event existingEvent = (Event) DBFunctions.getObject(searchEvent);
		return existingEvent;
	}

	public Member searchMember(String nameFirst, String nameLast) {
		Member searchMember = new Member(nameFirst, nameLast);
		Member existingMember = (Member) DBFunctions.getObject(searchMember);
		return existingMember;
	}

	public boolean isMemberAlreadyExisting(String nameFirst, String nameLast) {
		Member seachedMember = (Member) DBFunctions.getObject(new Member(
				nameFirst, nameLast));

		if (seachedMember == null) {
			return false;
		} else {
			return true;
		}
	}

	public User getLoggedInUser() {
		return loggedInUser;
	}

	public void setLoggedInUser(User loggedInUser) {
		this.loggedInUser = loggedInUser;
	}

	public void createMember(String nameFirst, String nameLast, Long date,
			Address newAddress, String email, String phoneHome,
			String phoneMobile, String state, String position, String role) {

		Member newMember = new Member(nameFirst, nameLast, date, newAddress,
				email, phoneHome, phoneMobile, state, position, role);

		createStandardAccountForUserAndMember(newMember);

	}

	private void createStandardAccountForUserAndMember(Member newMember) {

		ResourceBundle resBundle = BundleLoader.getResourceBundle();

		String username = newMember.getNameLast()
				+ newMember.getNameFirst().charAt(0);
		String password = newMember.getDateOfBirth().toString().substring(0, 7);

		String dialogTitle = resBundle.getString("successfull");
		String messageString = resBundle.getString("entryMadeSuccessfully")
				+ " \n\n" + resBundle.getString("followingAccountMade")
				+ "\n\n" + resBundle.getString("accountName") + username + "\n"
				+ resBundle.getString("accountPassword") + password;

		JOptionPane.showMessageDialog(null, messageString, dialogTitle,
				JOptionPane.INFORMATION_MESSAGE);

		password = PasswordFunctions.encodeString(password);
		User newUser = new User(username, password, newMember);

		DBFunctions.createObject(newUser);
	}

	public void updateMember(Address newAddress, String email,
			String phoneHome, String phoneMobile, String state,
			String position, String role) {

		Member existingMember = selectedMember;

		Boolean changingLoggedInUser = false;

		if (existingMember == this.loggedInUser.getMember()) {
			changingLoggedInUser = true;
		}

		ObjectContainer db = DBFunctions.getDB();

		Member editedMember = (Member) DBFunctions.getObjectForUpdate(db,
				existingMember);

		editedMember.setAddress(newAddress);
		editedMember.seteMail(email);
		editedMember.setPhoneHome(phoneHome);
		editedMember.setPhoneMobile(phoneMobile);
		editedMember.setState(state);
		editedMember.setPosition(position);
		editedMember.setRole(role);

		DBFunctions.updateObject(db, editedMember);
		DBFunctions.closeDB(db);

		if (changingLoggedInUser) {
			this.loggedInUser.setMember(editedMember);
		}
	}

	public ValueStringPair[] getWeekdaysValues() {
		return ValuesForComboBoxes.getWeekdaysValues();
	}

	public void deleteMember() {

		Member deleteMember = selectedMember;
		DBFunctions.deleteMember(deleteMember);
	}

	public void createTrainingPlan(Long startDate, Long endDate, int[] days,
			int[] times) {

		ArrayList<Training> newTrainings = new ArrayList<Training>();

		while (startDate < endDate) {

			Calendar calendar = new GregorianCalendar();
			Date dateTime = new Date(startDate);
			calendar.setTime(dateTime);

			int actualDay = calendar.get(Calendar.DAY_OF_WEEK);

			for (int dayIndex = 0; dayIndex < days.length; dayIndex++) {

				if (days[dayIndex] == actualDay) {

					Long trainingDayAsLong = calendar.getTimeInMillis() + 1000
							* 60 * 60 * times[dayIndex];

					newTrainings.add(new Training(trainingDayAsLong, null,
							null, Constants.TRAINING_NORMAL));
				}
			}
			startDate += 1000 * 60 * 60 * 24;
		}
		DBFunctions.createTrainingWithStandardParticpants(newTrainings);
	}

	public Vector<String> getParticipantsAttributeNames() {
		return Participant.attributeNamesShort();
	}

	public ArrayList<Member> getMemberEntriesAsArrayList() {
		return DBFunctions.getMemberEntriesAsArrayList();
	}

	public void updateParticipantsForEvent(ArrayList<Integer> selectedMembers,
			ArrayList<Integer> ratings) {
		Event event = DBFunctions.setNewParticipantsForEvent(selectedMembers,
				ratings, selectedEvent);
		this.selectedEvent = event;
	}

	public void setSelectedAddress(Address selectedAddress) {
		this.selectedAddress = selectedAddress;
	}

	public Address getSelectedAddress() {
		return selectedAddress;
	}

	public void getAddressFromIdentifier(String addressIdentifier) {
		ResourceBundle resBundle = BundleLoader.getResourceBundle();

		if (resBundle.getString("newAddress").equals(addressIdentifier)) {
			selectedAddress = null;
		} else {
			selectedAddress = DBFunctions
					.getAddressFromIdentifier(addressIdentifier);
		}
	}

	public void deleteEvent() {
		Event deleteEvent = selectedEvent;
		DBFunctions.deleteEvent(deleteEvent);
	}

	public void createEvent(Long date, Address newAddress) {

		ArrayList<Training> newTrainings = new ArrayList<Training>();
		newTrainings.add(new Training(date, newAddress, null,
				Constants.TRAINING_NORMAL));
		DBFunctions.createTrainingWithStandardParticpants(newTrainings);
	}

	public void updateEvent(Long date, Address newAddress) {

		Event existingEvent = selectedEvent;

		ObjectContainer db = DBFunctions.getDB();

		Event editedEvent = (Event) DBFunctions.getObjectForUpdate(db,
				existingEvent);

		Address foundAddress = (Address) DBFunctions.getObjectForUpdate(db,
				newAddress);

		if (foundAddress == null) {
			editedEvent.setPlace(newAddress);
		} else {
			editedEvent.setPlace(foundAddress);
		}

		editedEvent.setDate(date);

		DBFunctions.updateObject(db, editedEvent);
		DBFunctions.closeDB(db);
	}

	public ValueStringPair[] getRatingValues() {
		return ValuesForComboBoxes.getRatingValues();
	}

	public StringStringPair[] getStatsDescriptionValues() {
		return ValuesForComboBoxes.getStatsDescriptionValues();
	}

	public Statistic getAttendanceStat() {
		return DBFunctions.getAttendanceStat();
	}

	public Statistic getRatingAverageStat() {
		return DBFunctions.getRatingAverageStat();
	}
}
// TODO Falls das Model doch noch mal selber Events verschicken soll...
// this.addChangeListener(controller/view);
// fireModelChange(this);